package cn.xo68.boot.webgather.http;

import okhttp3.*;
import okhttp3.FormBody.Builder;
import org.apache.commons.lang3.StringUtils;

import javax.net.ssl.*;
import java.io.IOException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;

/**
 * http client
 *
 * @author wuyy cym
 * @date 2018年1月8日下午6:15:25
 */
public class HttpClient {

    public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
    public static final MediaType MIXED = MediaType.parse("multipart/mixed");
    public static final MediaType ALTERNATIVE = MediaType.parse("multipart/alternative");
    public static final MediaType DIGEST = MediaType.parse("multipart/digest");
    public static final MediaType PARALLEL = MediaType.parse("multipart/parallel");
    public static final MediaType FORM = MediaType.parse("multipart/form-data");
    public static final MediaType FORM_DEFAULT = MediaType.parse("application/x-www-form-urlencoded");

    private static HttpClient INSTANCE;
    private HttpClient(){}
    public static HttpClient getInstance(){
        return getInstance(20, 360, 360);
    }
    public static HttpClient getInstance(long connectTimeout, long writeTimeout, long readTimeout){
        if (INSTANCE == null) {
            INSTANCE = new HttpClient();
            INSTANCE.init(connectTimeout, writeTimeout, readTimeout);
        }
        return INSTANCE;
    }

    private final ConcurrentMap<String, List<Cookie>> cookieStore = new ConcurrentHashMap<String, List<Cookie>>();
    private OkHttpClient okHttpClient = null;

    /**
     * 初始化 okhttpClient
     *
     * @param connectTimeout 连接超时
     * @param writeTimeout   写入超时
     * @param readTimeout    读取超时
     */
    public void init(long connectTimeout, long writeTimeout, long readTimeout) {
        okHttpClient = new OkHttpClient.Builder().connectTimeout(connectTimeout, TimeUnit.SECONDS)// 连接超时(单位:秒)
                .writeTimeout(writeTimeout, TimeUnit.SECONDS)// 写入超时(单位:秒)
                .readTimeout(readTimeout, TimeUnit.SECONDS)// 读取超时(单位:秒)
                .pingInterval(20, TimeUnit.SECONDS) // websocket轮训间隔(单位:秒)
                .cookieJar(new CookieJar() {
                    @Override
                    public void saveFromResponse(HttpUrl httpUrl, List<Cookie> list) {
                        cookieStore.put(httpUrl.host(), list);
                    }

                    @Override
                    public List<Cookie> loadForRequest(HttpUrl httpUrl) {
                        List<Cookie> cookies = cookieStore.get(httpUrl.host());
                        return cookies != null ? cookies : new ArrayList<Cookie>();
                    }
                })
                .sslSocketFactory(createSSLSocketFactory())
                .hostnameVerifier(new TrustAllHostnameVerifier()).build();
    }

    private static class TrustAllCerts implements X509TrustManager {
        @Override
        public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        }

        @Override
        public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        }

        @Override
        public X509Certificate[] getAcceptedIssuers() {
            return new X509Certificate[0];
        }
    }

    private static class TrustAllHostnameVerifier implements HostnameVerifier {
        @Override
        public boolean verify(String hostname, SSLSession session) {
            return true;
        }
    }

    private static SSLSocketFactory createSSLSocketFactory() {
        SSLSocketFactory ssfFactory = null;

        try {
            SSLContext sc = SSLContext.getInstance("TLS");
            sc.init(null, new TrustManager[]{new TrustAllCerts()}, new SecureRandom());

            ssfFactory = sc.getSocketFactory();
        } catch (Exception e) {
        }

        return ssfFactory;
    }

    /**
     * 构建get请求地址
     *
     * @param url
     * @param parameters
     * @return
     */
    public String buildGetUrl(String url, Map<String, String> parameters) {
        if (StringUtils.isEmpty(url)) {
            return url;
        }
        StringBuilder urlSb = new StringBuilder();
        urlSb.append(url);
        if (parameters != null && !parameters.isEmpty()) {
            if (url.indexOf("?") < 0) {
                urlSb.append("?");
            }
            Boolean appendSplit = true;
            if (url.indexOf("?") >= url.length() - 1) {
                appendSplit = false;
            }

            for (Entry<String, String> entry : parameters.entrySet()) {
                if (!appendSplit) {
                    appendSplit = true;
                } else {
                    urlSb.append("&");
                }
                urlSb.append(MessageFormat.format("", entry.getKey(), entry.getValue()));
            }
        }
        return urlSb.toString();
    }

    /**
     * get 请求
     *
     * @param url
     * @param parameters
     * @param callback
     */
    public void get(String url, Map<String, String> parameters, HttpCallback callback) {
        if (StringUtils.isEmpty(url)) {
            callback.failure(new IOException("请求地址不能为空"), null);
            return;
        }
        url = buildGetUrl(url, parameters);
        Request request = new Request.Builder()
                .url(url)
                .build();
        Call call = okHttpClient.newCall(request);
        try {
            Response response = call.execute();
            callback.success(response, call);
            //System.out.println(response.body().string());
        } catch (IOException e) {
            //e.printStackTrace();
            callback.failure(e, call);
        }

    }

    /**
     * 异步get请求
     *
     * @param url
     * @param parameters
     * @param callback
     */
    public void asyncGet(String url, Map<String, String> parameters, final HttpCallback callback) {
        if (StringUtils.isEmpty(url)) {
            callback.failure(new IOException("请求地址不能为空"), null);
            return;
        }
        url = buildGetUrl(url, parameters);
        Request request = new Request.Builder()
                .url(url)
                .build();
        Call call = okHttpClient.newCall(request);
        call.enqueue(new Callback() {

            @Override
            public void onResponse(Call arg0, Response arg1) throws IOException {
                callback.success(arg1, arg0);
            }

            @Override
            public void onFailure(Call arg0, IOException arg1) {
                callback.failure(arg1, arg0);

            }
        });

    }

    /**
     * Post 请求
     *
     * @param url
     * @param parameters
     * @param callback
     */
    public void post(String url, Map<String, String> parameters, HttpCallback callback) {
        if (StringUtils.isEmpty(url)) {
            callback.failure(new IOException("请求地址不能为空"), null);
            return;
        }
        Builder builder = new FormBody.Builder();

        for (Entry<String, String> entry : parameters.entrySet()) {
            builder.add(entry.getKey(), entry.getValue());
        }
        RequestBody body = builder.build();

        postRequest(url, body, callback);
//
//        Request request = new Request.Builder()
//                .url(url)
//                .post(body)
//                .build();
//        Call call = okHttpClient.newCall(request);
//        try {
//            Response response = call.execute();
//            callback.success(response, call);
//            //System.out.println(response.body().string());
//        } catch (IOException e) {
//            //e.printStackTrace();
//            callback.failure(e, call);
//        }

    }

    /**
     * json格式 post提交信息
     *
     * @param url
     * @param jsonString
     * @param callback
     */
    public void postJson(String url, String jsonString, final HttpCallback callback) {
        if (StringUtils.isEmpty(url)) {
            callback.failure(new IOException("请求地址不能为空"), null);
            return;
        }
        RequestBody requestBody = RequestBody.create(JSON, jsonString);

        postRequest(url, requestBody, callback);

    }

    private void postRequest(String url, final RequestBody body, final HttpCallback callback) {
        Request request = new Request.Builder()
                .url(url)
                .post(body)
                .build();
        Call call = okHttpClient.newCall(request);
        try {
            Response response = call.execute();
            callback.success(response, call);
            //System.out.println(response.body().string());
        } catch (IOException e) {
            //e.printStackTrace();
            callback.failure(e, call);
        }
    }

    /**
     * 异步post请求
     *
     * @param url
     * @param parameters
     * @param callback
     */
    public void asyncPost(String url, Map<String, String> parameters, final HttpCallback callback) {
        if (StringUtils.isEmpty(url)) {
            callback.failure(new IOException("请求地址不能为空"), null);
            return;
        }
        Builder builder = new FormBody.Builder();

        for (Entry<String, String> entry : parameters.entrySet()) {
            builder.add(entry.getKey(), entry.getValue());
        }
        RequestBody body = builder.build();

        Request request = new Request.Builder()
                .url(url)
                .post(body)
                .build();
        Call call = okHttpClient.newCall(request);
        call.enqueue(new Callback() {

            @Override
            public void onResponse(Call arg0, Response arg1) throws IOException {
                callback.success(arg1, arg0);
            }

            @Override
            public void onFailure(Call arg0, IOException arg1) {
                callback.failure(arg1, arg0);

            }
        });

    }
}
